using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using RTMP.SimpleClient.Exceptions;

namespace RTMP.SimpleClient.Helpers
{
    /// <summary>
    /// AMF3 编码/解码助手类
    /// </summary>
    public static class AMF3Helper
    {
        /// <summary>
        /// AMF3 数据类型枚举
        /// </summary>
        public enum AMF3Type : byte
        {
            Undefined = 0x00,
            Null = 0x01,
            False = 0x02,
            True = 0x03,
            Integer = 0x04,
            Double = 0x05,
            String = 0x06,
            XMLDoc = 0x07,
            Date = 0x08,
            Array = 0x09,
            Object = 0x0A,
            XML = 0x0B,
            ByteArray = 0x0C
        }

        /// <summary>
        /// 编码 AMF3 值
        /// </summary>
        /// <param name="value">要编码的值</param>
        /// <returns>编码后的字节数组</returns>
        public static byte[] Encode(object? value)
        {
            using (var ms = new MemoryStream())
            using (var writer = new BinaryWriter(ms))
            {
                EncodeValue(writer, value);
                return ms.ToArray();
            }
        }

        /// <summary>
        /// 编码 AMF3 值到 Span
        /// </summary>
        /// <param name="buffer">输出缓冲区</param>
        /// <param name="value">要编码的值</param>
        /// <returns>写入的字节数</returns>
        public static int Encode(Span<byte> buffer, object? value)
        {
            return EncodeValueToSpan(buffer, value);
        }

        /// <summary>
        /// 解码 AMF3 值
        /// </summary>
        /// <param name="data">要解码的数据</param>
        /// <returns>解码后的对象</returns>
        public static object? Decode(byte[] data)
        {
            using (var ms = new MemoryStream(data))
            using (var reader = new BinaryReader(ms))
            {
                return DecodeValue(reader);
            }
        }

        /// <summary>
        /// 编码单个值
        /// </summary>
        private static void EncodeValue(BinaryWriter writer, object? value)
        {
            if (value == null)
            {
                writer.Write((byte)AMF3Type.Null);
                return;
            }

            switch (value)
            {
                case int i:
                    EncodeInteger(writer, i);
                    break;

                case double d:
                    writer.Write((byte)AMF3Type.Double);
                    WriteBigEndianDouble(writer, d);
                    break;

                case bool b:
                    writer.Write(b ? (byte)AMF3Type.True : (byte)AMF3Type.False);
                    break;

                case string s:
                    EncodeString(writer, s);
                    break;

                case Dictionary<string, object> dict:
                    EncodeObject(writer, dict);
                    break;

                case List<object> list:
                    EncodeArray(writer, list);
                    break;

                default:
                    // 对于不支持的类型，编码为 null
                    writer.Write((byte)AMF3Type.Null);
                    break;
            }
        }

        /// <summary>
        /// 编码单个值到 Span
        /// </summary>
        private static int EncodeValueToSpan(Span<byte> buffer, object? value)
        {
            int offset = 0;

            if (value == null)
            {
                if (buffer.Length < 1) throw new ArgumentException("Buffer too small");
                buffer[offset++] = (byte)AMF3Type.Null;
                return offset;
            }

            switch (value)
            {
                case int i:
                    offset += EncodeInteger(buffer.Slice(offset), i);
                    break;

                case double d:
                    if (buffer.Length < offset + 9) throw new ArgumentException("Buffer too small");
                    buffer[offset++] = (byte)AMF3Type.Double;
                    offset += WriteBigEndianDoubleToSpan(buffer.Slice(offset), d);
                    break;

                case bool b:
                    if (buffer.Length < offset + 1) throw new ArgumentException("Buffer too small");
                    buffer[offset++] = b ? (byte)AMF3Type.True : (byte)AMF3Type.False;
                    break;

                case string s:
                    offset += EncodeString(buffer.Slice(offset), s);
                    break;

                case Dictionary<string, object> dict:
                    offset += EncodeObject(buffer.Slice(offset), dict);
                    break;

                case List<object> list:
                    offset += EncodeArray(buffer.Slice(offset), list);
                    break;

                default:
                    // 对于不支持的类型，编码为 null
                    if (buffer.Length < 1) throw new ArgumentException("Buffer too small");
                    buffer[offset++] = (byte)AMF3Type.Null;
                    break;
            }

            return offset;
        }

        /// <summary>
        /// 编码字符串
        /// </summary>
        private static void EncodeString(BinaryWriter writer, string value)
        {
            writer.Write((byte)AMF3Type.String);

            if (string.IsNullOrEmpty(value))
            {
                writer.Write((byte)0x01); // 空字符串
                return;
            }

            var bytes = Encoding.UTF8.GetBytes(value);
            EncodeU29(writer, (uint)bytes.Length << 1 | 0x01); // 长度编码
            writer.Write(bytes);
        }

        /// <summary>
        /// 编码对象
        /// </summary>
        private static void EncodeObject(BinaryWriter writer, Dictionary<string, object> value)
        {
            writer.Write((byte)AMF3Type.Object);
            EncodeU29(writer, 0x0B); // 内联对象标记

            // 空类名
            EncodeString(writer, "");

            foreach (var kvp in value)
            {
                EncodeString(writer, kvp.Key);
                EncodeValue(writer, kvp.Value);
            }

            writer.Write((byte)0x01); // 对象结束标记
        }

        /// <summary>
        /// 编码数组
        /// </summary>
        private static void EncodeArray(BinaryWriter writer, List<object> value)
        {
            writer.Write((byte)AMF3Type.Array);
            EncodeU29(writer, (uint)value.Count << 1 | 0x01);

            // 空关联数组
            writer.Write((byte)0x01);

            foreach (var item in value)
            {
                EncodeValue(writer, item);
            }
        }

        /// <summary>
        /// 编码 U29 整数
        /// </summary>
        private static void EncodeU29(BinaryWriter writer, uint value)
        {
            if (value < 0x80)
            {
                writer.Write((byte)value);
            }
            else if (value < 0x4000)
            {
                writer.Write((byte)((value >> 7) | 0x80));
                writer.Write((byte)(value & 0x7F));
            }
            else if (value < 0x200000)
            {
                writer.Write((byte)((value >> 14) | 0x80));
                writer.Write((byte)((value >> 7) | 0x80));
                writer.Write((byte)(value & 0x7F));
            }
            else
            {
                writer.Write((byte)((value >> 22) | 0x80));
                writer.Write((byte)((value >> 15) | 0x80));
                writer.Write((byte)((value >> 8) | 0x80));
                writer.Write((byte)value);
            }
        }

        /// <summary>
        /// 编码整数（使用可变长度编码）
        /// </summary>
        private static void EncodeInteger(BinaryWriter writer, int value)
        {
            writer.Write((byte)AMF3Type.Integer);

            // AMF3 整数使用可变长度编码
            if (value < 0 || value >= 0x200000)
            {
                // 对于负数或大整数，使用双精度浮点数
                writer.Write((byte)AMF3Type.Double);
                writer.Write((double)value);
            }
            else
            {
                // 可变长度编码
                uint uvalue = (uint)value;
                if (uvalue < 0x80)
                {
                    writer.Write((byte)uvalue);
                }
                else if (uvalue < 0x4000)
                {
                    writer.Write((byte)((uvalue >> 7) | 0x80));
                    writer.Write((byte)(uvalue & 0x7F));
                }
                else if (uvalue < 0x200000)
                {
                    writer.Write((byte)((uvalue >> 14) | 0x80));
                    writer.Write((byte)((uvalue >> 7) | 0x80));
                    writer.Write((byte)(uvalue & 0x7F));
                }
                else
                {
                    writer.Write((byte)((uvalue >> 22) | 0x80));
                    writer.Write((byte)((uvalue >> 15) | 0x80));
                    writer.Write((byte)((uvalue >> 8) | 0x80));
                    writer.Write((byte)uvalue);
                }
            }
        }

        /// <summary>
        /// 编码整数到 Span（使用可变长度编码）
        /// </summary>
        private static int EncodeInteger(Span<byte> buffer, int value)
        {
            int offset = 0;
            if (buffer.Length < offset + 1) throw new ArgumentException("Buffer too small");
            buffer[offset++] = (byte)AMF3Type.Integer;

            // AMF3 整数使用可变长度编码
            if (value < 0 || value >= 0x200000)
            {
                // 对于负数或大整数，使用双精度浮点数
                if (buffer.Length < offset + 9) throw new ArgumentException("Buffer too small");
                buffer[offset++] = (byte)AMF3Type.Double;
                offset += WriteBigEndianDoubleToSpan(buffer.Slice(offset), (double)value);
            }
            else
            {
                // 可变长度编码
                uint uvalue = (uint)value;
                if (uvalue < 0x80)
                {
                    if (buffer.Length < offset + 1) throw new ArgumentException("Buffer too small");
                    buffer[offset++] = (byte)uvalue;
                }
                else if (uvalue < 0x4000)
                {
                    if (buffer.Length < offset + 2) throw new ArgumentException("Buffer too small");
                    buffer[offset++] = (byte)((uvalue >> 7) | 0x80);
                    buffer[offset++] = (byte)(uvalue & 0x7F);
                }
                else if (uvalue < 0x200000)
                {
                    if (buffer.Length < offset + 3) throw new ArgumentException("Buffer too small");
                    buffer[offset++] = (byte)((uvalue >> 14) | 0x80);
                    buffer[offset++] = (byte)((uvalue >> 7) | 0x80);
                    buffer[offset++] = (byte)(uvalue & 0x7F);
                }
                else
                {
                    if (buffer.Length < offset + 4) throw new ArgumentException("Buffer too small");
                    buffer[offset++] = (byte)((uvalue >> 22) | 0x80);
                    buffer[offset++] = (byte)((uvalue >> 15) | 0x80);
                    buffer[offset++] = (byte)((uvalue >> 8) | 0x80);
                    buffer[offset++] = (byte)uvalue;
                }
            }

            return offset;
        }

        /// <summary>
        /// 编码字符串到 Span
        /// </summary>
        private static int EncodeString(Span<byte> buffer, string value)
        {
            int offset = 0;
            if (buffer.Length < offset + 1) throw new ArgumentException("Buffer too small");
            buffer[offset++] = (byte)AMF3Type.String;

            if (string.IsNullOrEmpty(value))
            {
                if (buffer.Length < offset + 1) throw new ArgumentException("Buffer too small");
                buffer[offset++] = 0x01; // 空字符串
                return offset;
            }

            var bytes = Encoding.UTF8.GetBytes(value);
            offset += EncodeU29ToSpan(buffer.Slice(offset), (uint)bytes.Length << 1 | 0x01); // 长度编码
            if (buffer.Length < offset + bytes.Length) throw new ArgumentException("Buffer too small");
            bytes.CopyTo(buffer.Slice(offset));
            offset += bytes.Length;
            return offset;
        }

        /// <summary>
        /// 编码对象到 Span
        /// </summary>
        private static int EncodeObject(Span<byte> buffer, Dictionary<string, object> value)
        {
            int offset = 0;
            if (buffer.Length < offset + 1) throw new ArgumentException("Buffer too small");
            buffer[offset++] = (byte)AMF3Type.Object;
            offset += EncodeU29ToSpan(buffer.Slice(offset), 0x0B); // 内联对象标记

            // 空类名
            offset += EncodeString(buffer.Slice(offset), "");

            foreach (var kvp in value)
            {
                offset += EncodeString(buffer.Slice(offset), kvp.Key);
                offset += EncodeValueToSpan(buffer.Slice(offset), kvp.Value);
            }

            if (buffer.Length < offset + 1) throw new ArgumentException("Buffer too small");
            buffer[offset++] = 0x01; // 对象结束标记
            return offset;
        }

        /// <summary>
        /// 编码数组到 Span
        /// </summary>
        private static int EncodeArray(Span<byte> buffer, List<object> value)
        {
            int offset = 0;
            if (buffer.Length < offset + 1) throw new ArgumentException("Buffer too small");
            buffer[offset++] = (byte)AMF3Type.Array;
            offset += EncodeU29ToSpan(buffer.Slice(offset), (uint)value.Count << 1 | 0x01);

            // 空关联数组
            if (buffer.Length < offset + 1) throw new ArgumentException("Buffer too small");
            buffer[offset++] = 0x01;

            foreach (var item in value)
            {
                offset += EncodeValueToSpan(buffer.Slice(offset), item);
            }

            return offset;
        }

        /// <summary>
        /// 编码 U29 整数到 Span
        /// </summary>
        private static int EncodeU29ToSpan(Span<byte> buffer, uint value)
        {
            int offset = 0;
            if (value < 0x80)
            {
                if (buffer.Length < offset + 1) throw new ArgumentException("Buffer too small");
                buffer[offset++] = (byte)value;
            }
            else if (value < 0x4000)
            {
                if (buffer.Length < offset + 2) throw new ArgumentException("Buffer too small");
                buffer[offset++] = (byte)((value >> 7) | 0x80);
                buffer[offset++] = (byte)(value & 0x7F);
            }
            else if (value < 0x200000)
            {
                if (buffer.Length < offset + 3) throw new ArgumentException("Buffer too small");
                buffer[offset++] = (byte)((value >> 14) | 0x80);
                buffer[offset++] = (byte)((value >> 7) | 0x80);
                buffer[offset++] = (byte)(value & 0x7F);
            }
            else
            {
                if (buffer.Length < offset + 4) throw new ArgumentException("Buffer too small");
                buffer[offset++] = (byte)((value >> 22) | 0x80);
                buffer[offset++] = (byte)((value >> 15) | 0x80);
                buffer[offset++] = (byte)((value >> 8) | 0x80);
                buffer[offset++] = (byte)value;
            }
            return offset;
        }

        /// <summary>
        /// 写入大端字节序的double值到 Span
        /// </summary>
        private static int WriteBigEndianDoubleToSpan(Span<byte> buffer, double value)
        {
            if (buffer.Length < 8) throw new ArgumentException("Buffer too small for double");
            var bytes = BitConverter.GetBytes(value);
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes);
            }
            bytes.CopyTo(buffer);
            return 8;
        }

        /// <summary>
        /// 解码单个值
        /// </summary>
        private static object? DecodeValue(BinaryReader reader)
        {
            var type = (AMF3Type)reader.ReadByte();

            switch (type)
            {
                case AMF3Type.Null:
                    return null;

                case AMF3Type.False:
                    return false;

                case AMF3Type.True:
                    return true;

                case AMF3Type.Integer:
                    return DecodeU29(reader);

                case AMF3Type.Double:
                    return ReadBigEndianDouble(reader);

                case AMF3Type.String:
                    return DecodeString(reader);

                case AMF3Type.Object:
                    return DecodeObject(reader);

                case AMF3Type.Array:
                    return DecodeArray(reader);

                default:
                    throw new RTMPUnsupportedException($"AMF3 type {type} is not supported");
            }
        }

        /// <summary>
        /// 解码 U29 整数
        /// </summary>
        private static int DecodeU29(BinaryReader reader)
        {
            int result = 0;
            int shift = 0;
            byte b;

            do
            {
                b = reader.ReadByte();
                result |= (b & 0x7F) << shift;
                shift += 7;
            } while ((b & 0x80) != 0 && shift < 32);

            return result;
        }

        /// <summary>
        /// 解码字符串
        /// </summary>
        private static string DecodeString(BinaryReader reader)
        {
            int length = DecodeU29(reader) >> 1;
            if (length == 0)
            {
                return string.Empty;
            }

            var bytes = reader.ReadBytes(length);
            return Encoding.UTF8.GetString(bytes);
        }

        /// <summary>
        /// 解码对象
        /// </summary>
        private static Dictionary<string, object?> DecodeObject(BinaryReader reader)
        {
            var result = new Dictionary<string, object?>();
            DecodeU29(reader); // 跳过对象引用

            // 跳过类定义
            DecodeString(reader); // 类名

            while (true)
            {
                var key = DecodeString(reader);
                if (string.IsNullOrEmpty(key))
                {
                    break;
                }

                var value = DecodeValue(reader);
                result[key] = value;
            }

            return result;
        }

        /// <summary>
        /// 解码数组
        /// </summary>
        private static List<object?> DecodeArray(BinaryReader reader)
        {
            var result = new List<object?>();
            int length = DecodeU29(reader) >> 1;

            // 跳过关联数组部分
            while (!string.IsNullOrEmpty(DecodeString(reader))) { }

            for (int i = 0; i < length; i++)
            {
                result.Add(DecodeValue(reader));
            }

            return result;
        }

        /// <summary>
        /// 写入大端字节序的double值
        /// </summary>
        private static void WriteBigEndianDouble(BinaryWriter writer, double value)
        {
            var bytes = BitConverter.GetBytes(value);
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes);
            }
            writer.Write(bytes);
        }

        /// <summary>
        /// 读取大端字节序的double值
        /// </summary>
        private static double ReadBigEndianDouble(BinaryReader reader)
        {
            var bytes = reader.ReadBytes(8);
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes);
            }
            return BitConverter.ToDouble(bytes, 0);
        }
    }
}
